package cn.dansj.controller;

import cn.dansj.common.redis.annotation.cache.RemoveCache;
import cn.dansj.common.redis.annotation.cache.UseCache;
import cn.dansj.common.redis.annotation.lock.Lock4j;
import cn.dansj.common.request.annotation.http.GetMethod;
import cn.dansj.common.request.annotation.http.PostMethod;
import cn.dansj.common.request.annotation.http.RequestMappingMethod;
import cn.dansj.common.request.annotation.http.RequestParam;
import cn.dansj.common.request.RequestUtils;
import cn.dansj.common.utils.json.DictMap;
import cn.dansj.common.utils.bean.Return;
import cn.dansj.common.utils.enums.BooleanType;
import cn.dansj.common.redis.RedisUtils;
import cn.dansj.common.utils.transfer.*;
import cn.dansj.utils.project.ProjectUtils;
import cn.dansj.utils.tencent.LightHouseUtils;
import com.tencentcloudapi.common.AbstractModel;
import com.tencentcloudapi.dnspod.v20210323.models.CreateRecordRequest;
import com.tencentcloudapi.dnspod.v20210323.models.ModifyRecordRequest;
import com.tencentcloudapi.lighthouse.v20200324.models.FirewallRule;
import com.tencentcloudapi.lighthouse.v20200324.models.FirewallRuleInfo;
import com.tencentcloudapi.ssl.v20191205.models.ApplyCertificateRequest;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RequestMappingMethod("/tencentCloud")
public class TencentCloudController {
    private final RedisUtils redisUtils;

    public TencentCloudController(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
        LightHouseUtils.setOperationCallback((primaryKey, type, response) -> redisUtils.hmset(primaryKey + "@latest", DictMap.from(redisUtils.hgetall(primaryKey + "@latest")).put(type, AbstractModel.toJsonString(response))));
    }

    private static Return<?> transferReturnData(DictMap data) {
        return Return.T(Transformation.castToBoolean(data.remove("responseStatus")), Transformation.toString(data.remove("message"))).setData(data);
    }

    private static Return<?> transferReturnData(List<DictMap> data) {
        final List<String> message = data.stream().filter(e -> !e.getBooleanValue("responseStatus")).map(e -> e.getString("instance") + " : " + e.getString("message")).toList();
        final boolean responseStatus = data.stream().allMatch(e -> e.getBooleanValue("responseStatus"));
        return Return.T(responseStatus, responseStatus ? "" : String.join("\n", message)).setData(data);
    }

    @GetMethod("/describeFirewallRules")
    public Return<?> describeFirewallRules(@RequestParam String instance, @RequestParam String instanceId) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).describeFirewallRules(instanceId));
    }

    @PostMethod("/deleteFirewallRule")
    public Return<?> deleteFirewallRule(@RequestParam String instance, @RequestParam String instanceId, @RequestParam("removeRule") FirewallRule removeRule) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).deleteFirewallRules(instanceId, removeRule));
    }

    @PostMethod("/createFirewallRule")
    public Return<?> createFirewallRule(@RequestParam String instance, @RequestParam String instanceId, @RequestParam("newRule") FirewallRule newRule) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).createFirewallRules(instanceId, newRule));
    }

    @PostMethod("/modifyFirewallRule")
    @Lock4j
    public Return<?> modifyFirewallRule(@RequestParam String instance, @RequestParam String instanceId, @RequestParam("newRule") FirewallRuleInfo newRule, @RequestParam("backRule") FirewallRuleInfo backRule) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        boolean exists = false;
        final LightHouseUtils tencentCloudClient = ProjectUtils.getTencentCloudClient(instance);
        List<FirewallRuleInfo> ruleSet = tencentCloudClient.describeFirewallRules(instanceId).getList("FirewallRuleSet", FirewallRuleInfo.class);
        List<FirewallRule> newRuleSet = new ArrayList<>();
        final String backRuleString = DictMap.toJSONString(backRule);
        for (FirewallRuleInfo firewallRuleInfo : ruleSet) {
            final boolean equals = DictMap.toJSONString(firewallRuleInfo).equals(backRuleString);
            if (equals) {
                exists = true;
                final FirewallRule firewallRule = new FirewallRule();
                firewallRule.setFirewallRuleDescription(newRule.getFirewallRuleDescription());
                firewallRule.setProtocol(newRule.getProtocol());
                firewallRule.setPort(newRule.getPort());
                firewallRule.setSkipSign(newRule.getSkipSign());
                firewallRule.setAction(newRule.getAction());
                firewallRule.setCidrBlock(newRule.getCidrBlock());
                newRuleSet.add(firewallRule);
            } else {
                final FirewallRule firewallRule = new FirewallRule();
                firewallRule.setFirewallRuleDescription(firewallRuleInfo.getFirewallRuleDescription());
                firewallRule.setProtocol(firewallRuleInfo.getProtocol());
                firewallRule.setPort(firewallRuleInfo.getPort());
                firewallRule.setSkipSign(firewallRuleInfo.getSkipSign());
                firewallRule.setAction(firewallRuleInfo.getAction());
                firewallRule.setCidrBlock(firewallRuleInfo.getCidrBlock());
                newRuleSet.add(firewallRule);
            }
        }
        if (exists) {
            return transferReturnData(tencentCloudClient.modifyFirewallRules(instanceId, newRuleSet));
        }

        return Return.T(false, "当前规则已不存在,刷新后重试");
    }

    @GetMethod("/describeInstance")
    @UseCache(timeUnit = TimeUnit.DAYS, expire = 10)
    public Return<?> describeInstance() {
        return transferReturnData(ProjectUtils.getTencentCloudInstance().stream().map(e -> ProjectUtils.getTencentCloudClient(e).describeInstances().put("instance", e)).toList());
    }

    @GetMethod("/instanceVncUrl")
    public Return<?> describeInstanceVncUrl(@RequestParam String instance, @RequestParam String instanceId) {
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).describeInstanceVncUrl(instanceId));
    }

    @GetMethod("/describeDomain")
    @UseCache(timeUnit = TimeUnit.DAYS, expire = 10)
    public Return<?> describeDomain() {
        return transferReturnData(ProjectUtils.getTencentCloudInstance().stream().map(e -> ProjectUtils.getTencentCloudClient(e).describeDomain().put("instance", e)).toList());
    }

    @GetMethod("/describeRecordList")
    @UseCache(reference = "${#method}.${#domain}")
    public Return<?> describeRecordList(@RequestParam String instance, @RequestParam String domain) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).describeRecordList(domain));
    }

    @PostMethod("/deleteRecord")
    @RemoveCache(reference = {"cn.dansj.controller.TencentCloudController.describeRecordList.${#domain}", "cn.dansj.controller.TencentCloudController.describeDomain"},
            remark = "更新成功后删除获取解析记录的缓存以及域名记录缓存")
    public Return<?> deleteRecord(@RequestParam String instance, @RequestParam String domain, @RequestParam long recordId) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).deleteRecord(domain, recordId));
    }

    @PostMethod("/createRecord")
    @RemoveCache(reference = {"cn.dansj.controller.TencentCloudController.describeRecordList.${#createRecordRequest.Domain}", "cn.dansj.controller.TencentCloudController.describeDomain"},
            remark = "更新成功后删除获取解析记录的缓存以及域名记录缓存")
    public Return<?> createRecord(@RequestParam String instance, @RequestParam CreateRecordRequest createRecordRequest) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        final Long weight = createRecordRequest.getWeight();
        createRecordRequest.setWeight(weight == null || weight < 0 ? null : weight);
        createRecordRequest.setSubDomain(Transformation.nvl(createRecordRequest.getSubDomain(), "@"));
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).createRecord(createRecordRequest));
    }

    @PostMethod("/modifyRecord")
    @RemoveCache(reference = "cn.dansj.controller.TencentCloudController.describeRecordList.${#modifyRecordRequest.Domain}", remark = "更新成功后删除获取解析记录的缓存")
    public Return<?> modifyRecord(@RequestParam String instance, @RequestParam ModifyRecordRequest modifyRecordRequest) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        final Long weight = modifyRecordRequest.getWeight();
        modifyRecordRequest.setWeight(weight == null || weight < 0 ? null : weight);
        modifyRecordRequest.setSubDomain(Transformation.nvl(modifyRecordRequest.getSubDomain(), "@"));
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).modifyRecord(modifyRecordRequest));
    }

    @PostMethod("/modifyRecordStatus")
    @RemoveCache(reference = "cn.dansj.controller.TencentCloudController.describeRecordList.${#domain}", remark = "更新成功后删除获取解析记录的缓存")
    public Return<?> modifyRecordStatus(@RequestParam String instance, @RequestParam String domain, @RequestParam long recordId, @RequestParam String status) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).modifyRecordStatus(domain, recordId, status));
    }

    @GetMethod("/describeCertificates")
    @UseCache
    public Return<?> describeCertificates() {
        return transferReturnData(ProjectUtils.getTencentCloudInstance().stream().map(e -> ProjectUtils.getTencentCloudClient(e).describeCertificates().put("instance", e)).toList());
    }

    @GetMethod("/describeCertificateDetail")
    @UseCache
    public Return<?> describeCertificateDetail(@RequestParam String instance, @RequestParam String certificateId) {
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).describeCertificateDetail(certificateId));
    }

    @PostMethod("/createCertificate")
    @RemoveCache(reference = "cn.dansj.controller.TencentCloudController.describeCertificates", remark = "创建成功后清空获取证书的缓存")
    public Return<?> createCertificate(@RequestParam String instance, @RequestParam ApplyCertificateRequest certificateRequest, @RequestParam boolean batch) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        if (batch) {
            ProjectUtils.getTencentCloudClient(instance).batchCreateCertificate(certificateRequest);
            return Return.T(true, "批量创建证书请求已提交, 稍后请手动刷新获取证书申请状态");
        }
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).createCertificate(certificateRequest));
    }

    @PostMethod("/cancelAuditCertificate")
    @RemoveCache(reference = "cn.dansj.controller.TencentCloudController.describeCertificates", remark = "取消验证后清空获取证书的缓存")
    public Return<?> cancelAuditCertificate(@RequestParam String instance, @RequestParam String certificateId) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        return transferReturnData(ProjectUtils.getTencentCloudClient(instance).cancelAuditCertificate(certificateId));
    }

    /**
     * 删除ssl证书记录
     *
     * @param instance      实例
     * @param certificateId 证书ID
     */
    @PostMethod("/deleteCertificate")
    @RemoveCache(reference = "cn.dansj.controller.TencentCloudController.describeCertificates", remark = "删除成功后清空获取证书的缓存")
    public Return<?> deleteCertificate(@RequestParam String instance, @RequestParam String certificateId, @RequestParam boolean batch) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        final LightHouseUtils tencentCloudClient = ProjectUtils.getTencentCloudClient(instance);
        if (batch) {
            final List<String> certificateIds = ArrayUtils.asList(certificateId.split(","));
            final int success = tencentCloudClient.batchDeleteCertificate(certificateIds);
            return Return.T(true, success == certificateIds.size() ? "证书批量删除成功" : "证书批量删除成功" + success + "张, 失败" + (certificateIds.size() - success) + "张");
        }
        return transferReturnData(tencentCloudClient.deleteCertificate(certificateId));
    }

    /**
     * 吊销ssl证书
     *
     * @param instance      实例
     * @param certificateId 证书ID
     */
    @PostMethod("/revokeCertificate")
    @RemoveCache(reference = "cn.dansj.controller.TencentCloudController.describeCertificates", remark = "吊销成功后清空获取证书的缓存")
    public Return<?> revokeCertificate(@RequestParam String instance, @RequestParam String certificateId, @RequestParam boolean batch) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        final LightHouseUtils tencentCloudClient = ProjectUtils.getTencentCloudClient(instance);
        if (batch) {
            tencentCloudClient.batchRevokeCertificate(ArrayUtils.asList(certificateId.split(",")));
            return Return.T(true, "批量吊销证书请求已提交, 稍后请手动刷新获取证书最新状态");
        }
        return transferReturnData(tencentCloudClient.revokeCertificate(certificateId));
    }

    @GetMethod("/revokeCertificateAuth")
    public Return<?> revokeCertificateAuth(@RequestParam String certificateId) {
        return Return.T(DictMap.from(redisUtils.hgetall("certificateId@" + certificateId + "@latest").get("revokeCertificate")));
    }

    /**
     * 获取下载证书链接
     *
     * @param instance      实例
     * @param certificateId 证书ID
     */
    @GetMethod("/describeDownloadCertificateUrl")
    public Return<?> describeDownloadCertificateUrl(@RequestParam String instance, @RequestParam String certificateId, @RequestParam String serviceType) {
        AssertUtils.notNull(instance, "instance参数不能为空");
        final DictMap data = ProjectUtils.getTencentCloudClient(instance).describeDownloadCertificateUrl(certificateId, serviceType);
        final String downloadCertificateUrl = data.getString("DownloadCertificateUrl");
        final String downloadFilename = data.getString("DownloadFilename");
        final String requestId = data.getString("RequestId");
        redisUtils.hmset("certificateDownload@" + requestId, DictMap.of("url", downloadCertificateUrl, "filename", downloadFilename), 5);
        AssertUtils.state(Verification.checkAllNotNull(downloadCertificateUrl, downloadFilename, requestId) && data.getBooleanValue("responseStatus"), "请求失败");
        return Return.T(requestId);
    }

    /**
     * 下载证书文件
     *
     * @param requestId 请求ID
     */
    @GetMethod(value = "/downloadCertificate/{requestId}", permission = BooleanType.TRUE)
    public void downloadCertificate(@RequestParam(path = true) String requestId) {
        final Map<Object, Object> map = redisUtils.hgetall("certificateDownload@" + requestId);
        final Object url = map.get("url");
        final Object name = map.get("filename");
        AssertUtils.state(Verification.checkAllNotNull(url, name), "证书文件不存在或已过期");
        RequestUtils.transferUrlToResponse(url.toString(), name.toString());
    }

    /**
     * 部署证书
     *
     * @param instance      实例名称
     * @param certificateId 证书ID
     * @param instanceId    实例ID
     */
    @PostMethod("/deployCertificate")
    public Return<?> deployCertificate(@RequestParam String instance, @RequestParam String certificateId, @RequestParam String instanceId) throws InterruptedException {
        AssertUtils.notNull(instance, "instance参数不能为空");
        final DictMap tencentCloudConfig = ProjectUtils.getSettingByRedis("tencent.cloud");
        final String dataDir = tencentCloudConfig.getString("data.dir");
        final String nginxDir = tencentCloudConfig.getString("nginx.dir");
        final boolean removeZip = tencentCloudConfig.getBooleanValue("certificate.zip.remove");
        final boolean autoReload = tencentCloudConfig.getBooleanValue("deploy.ssl.auto.reload.nginx");
        final String cmdNginxReload = autoReload ? "nginx -s reload" : null;
        final boolean deploySuccess = ProjectUtils.getTencentCloudClient(instance).deployCertificate(instanceId, certificateId, "nginx", dataDir, nginxDir, removeZip, cmdNginxReload);
        return Return.T(deploySuccess, deploySuccess ? "部署成功" : "部署失败");
    }

    /**
     * 获取配置
     */
    @GetMethod("/getSetting")
    public Return<?> getTencentCloudSetting() {
        return Return.T(ProjectUtils.getSettingByRedis("tencent.cloud").filter(e -> !e.getKey().startsWith("account.")));
    }

    /**
     * 获取证书操作日志
     */
    @GetMethod(value = "/describeCertificateOperateLogs")
    public Return<?> describeCertificateOperateLogs() {
        return Return.T(ProjectUtils.getTencentCloudInstance().stream().flatMap(e -> ProjectUtils.getTencentCloudClient(e).describeCertificateOperateLogs().getList("OperateLogs", DictMap.class).stream().map(k -> k.put("Instance", e))).toList());
    }
}
